package com.gitee.beiding.template_excel;

import jdk.nashorn.api.scripting.ScriptObjectMirror;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Js {

    private static List<String> functions = new ArrayList<>();

    private static Map<String, Object> ng;

    /**
     * 单例懒加载
     */
    private static ScriptEngine engine;

    private static void init() {

        if (engine == null) {
            synchronized (Js.class) {
                if (engine == null) {
                    //创建线程
                    ScriptEngineManager manager = new ScriptEngineManager();
                    engine = manager.getEngineByName("nashorn");
                    try {
                        InputStream stream = Js.class.getClassLoader().getResource("template-excel-init.js").openConnection().getInputStream();
                        String initJs = FileUtils.read(stream);
                        SimpleBindings simpleBindings = new SimpleBindings();
                        engine.eval(initJs, simpleBindings);
                        ng = (Map<String, Object>) simpleBindings.get("nashorn.global");

                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }


        if (!RenderContext.getCurrent().initialized()) {
            try {
                RenderContext.getCurrent().getJsBinds().put("console", new Console());
                RenderContext.getCurrent().getJsBinds().put("_java", new Java());
                RenderContext.getCurrent().getJsBinds().put("nashorn.global", ng);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            RenderContext.getCurrent().setInitialized();
        }

    }

    //不能直接执行某些代码,防止引擎内部声明污染
    static Object exe(String js) {
        try {
            init();
            return engine.eval(js, RenderContext.getCurrent().getJsBinds());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    public static void defineCommand(String s) {

        synchronized (Js.class) {
            if (functions == null) {
                throw new RuntimeException("请在Js使用前定义函数");
            }
        }

        String d1 = s.trim();

        if (!d1.startsWith("function")) {
            throw new RuntimeException("指令定义无效,必须以function开头");
        }

        String d2 = d1.substring(8).trim();

        System.out.println(d2);

        if (!d2.startsWith("$")) {
            throw new RuntimeException("指令名称必须以'$'开头");
        }

        functions.add(s);
    }


    public static Object call(String method, Object... p) {
        init();
        try {
            RenderContext.getCurrent().getJsBinds().put("__args__", p);
            engine.eval(method+".apply(this,__args__);", RenderContext.getCurrent().getJsBinds());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            RenderContext.getCurrent().getJsBinds().remove("__args__");
        }
        return null;
    }

    public static Object set(String k, Object v) {
        return RenderContext.getCurrent().getJsBinds().put(k, v);
    }


    public static Object put(String k, Object v) {
        return RenderContext.getCurrent().getJsBinds().put(k, v);
    }

    /**
     * 由于引擎是线程隔离的,调用该方法定义的函数只对晚于调用该方法初始化的引擎才是有效的
     *
     * @param s 定义的函数
     */

//定义一个函数
    public static void defineFunction(String s) {

        synchronized (Js.class) {
            if (functions == null) {
                throw new RuntimeException("请在Js使用前定义函数");
            }
        }


        String d1 = s.trim();

        if (!d1.startsWith("function")) {
            throw new RuntimeException("指令定义无效,必须以function开头");
        }

        String d2 = d1.trim();

        if (d2.startsWith("$")) {
            throw new RuntimeException("函数名称不能以'$'开头");
        }

        functions.add(FunctionUtils.handleCacheableFunction(s));
    }

    public static Object remove(String k) {
        return RenderContext.getCurrent().getJsBinds().remove(k);
    }

    public static Object get(String k) {
        return RenderContext.getCurrent().getJsBinds().get(k);
    }

    public static void putAll(Map<String, ?> all) {
        RenderContext.getCurrent().getJsBinds().putAll(all);
    }

    //提供控制台相关的功能
    public static class Console {
        public void log(Object obj) {
            if (obj instanceof ScriptObjectMirror) {
                System.out.println(call("objToString", obj));
            } else {
                System.out.println(obj);
            }
        }

        public void log() {
            System.out.println();
        }
    }

    //提供缓存操作
    private static class CacheTree {

        private static class Node {
            private Map<Object, Node> subMap;
            private Object value;
        }

        //TODO  第一级参数
        private Map<Object, Node> subMap;

        private Object value;

        void set(Object[] args, Object data) {
            if (args.length == 0) {
                value = data;
                return;
            }

            if (subMap == null) {
                subMap = new HashMap<>();
            }

            //从第一级参数开始递归
            Map<Object, Node> sm = subMap;

            Node node = null;

            //一直找到最后一级参数
            for (Object key : args) {
                node = sm.computeIfAbsent(key, k -> new Node());
                if (node.subMap == null) {
                    node.subMap = new HashMap<>();
                }
                sm = node.subMap;
            }

            node.value = data;

        }

        Object get(Object[] args) {
            if (args.length == 0) {
                return value;
            }
            if (subMap == null) {
                return null;
            }
            Map<Object, Node> sm = subMap;

            Node node = null;

            //一直找到最后一级参数
            for (Object key : args) {
                node = sm.get(key);
                if (node == null || node.subMap == null) {
                    return null;
                }
                sm = node.subMap;
            }

            return node.value;
        }

    }

    //对js提供java底层的操作
    public static class Java {

        //缓存数
        private Map<String, CacheTree> treeMap = new HashMap<>();

        private Map<String, Object> blankArgCacheMap = new HashMap<>();
        private Map<String, Object> oneArgCacheMap = new HashMap<>();

        public void cachePut(String name, Object[] args, Object data) {
            CacheTree cacheTree = treeMap.computeIfAbsent(name, k -> new CacheTree());
            cacheTree.set(args, data);
        }

        public Object cacheGet(String name, Object[] args) {

            CacheTree cacheTree = treeMap.get(name);
            if (cacheTree == null) {
                return null;
            }

            return cacheTree.get(args);
        }

        //清空现有缓存
        public void clearCache() {
            treeMap = new HashMap<>();
            blankArgCacheMap = new HashMap<>();
            oneArgCacheMap = new HashMap<>();
            //辅助gc
//            System.gc();

        }

        public int createIndex(Integer n, int index) {

            if (n == null) {
                return index;
            }

            return n + index;
        }

        public Object getBlankArgCacheMap() {
            return blankArgCacheMap;
        }

        public Object getOneArgCacheMap() {
            return oneArgCacheMap;
        }

        public void setBlankArgCacheMap(Map<String, Object> blankArgCacheMap) {
            this.blankArgCacheMap = blankArgCacheMap;
        }

        public void setOneArgCacheMap(Map<String, Object> oneArgCacheMap) {
            this.oneArgCacheMap = oneArgCacheMap;
        }
    }

}

